package com.clover.redis;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * reids操作类
 * Created by sanyecao on 2017/10/17.
 */
@Component
public class RedisManager {

    private static final Logger logger = LoggerFactory.getLogger(RedisManager.class);

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * redis值过期机制：
     * Redis无论有没有设置expire，他都会遵循redis的配置好的删除机制，在配置文件里设置：
     redis最大内存不足"时,数据清除策略,默认为"volatile-lru"。

     volatile-lru  ->对"过期集合"中的数据采取LRU(近期最少使用)算法.如果对key使用"expire"指令指定了过期时间,那么此key将会被添加到"过期集合"中。将已经过期/LRU的数据优先移除.如果"过期集合"中全部移除仍不能满足内存需求,将OOM.
     allkeys-lru ->对所有的数据,采用LRU算法

     volatile-random ->对"过期集合"中的数据采取"随即选取"算法,并移除选中的K-V,直到"内存足够"为止. 如果如果"过期集合"中全部移除全部移除仍不能满足,将OOM
     allkeys-random ->对所有的数据,采取"随机选取"算法,并移除选中的K-V,直到"内存足够"为止

     volatile-ttl ->对"过期集合"中的数据采取TTL算法(最小存活时间),移除即将过期的数据.
     noeviction ->不做任何干扰操作,直接返回OOM异常
     另外，如果数据的过期不会对"应用系统"带来异常,且系统中write操作比较密集,建议采取"allkeys-lru"。

     由以上可以看出，对没设置expire的数据，产生影响的是allkeys-lru机制，allkeys-random。

     所以redis没设置expire的数据是否会删除，是由你自己选择的删除机制决定的。
     */


    /**************************************************************************************添加*************************************************************************/
    /**
     * 添加string
     *    1.如果key值存在，则会覆盖原来的值
     *    2.如果key值不存在，则会新建并放入obj值
     * @param key 键
     * @param obj 值
     * @param validTime 有效时间 （单位：秒）
     * @return
     */
    public boolean insertObject(String key ,Object obj ,long validTime){
        try {
            final String value = JSONObject.toJSONString(obj);

            //执行redis操作
            boolean result = redisTemplate.execute((RedisConnection redisConnection) -> {

                byte[] redisKey = redisTemplate.getStringSerializer().serialize(key);
                byte[] redisValue = redisTemplate.getStringSerializer().serialize(value);

                if (validTime > 0){
                    //插入redis，带有效期
                    redisConnection.setEx(redisKey, validTime, redisValue);
                }else {
                    //插入redis，无有效期
                    redisConnection.set(redisKey, redisValue);
                }
                return true;
            });

            return result;
        } catch (Exception e){
            logger.error("添加redis异常", e);
        }
        return false;
    }

    /**
     * 添加string
     * @param key 键
     * @param obj 值
     * @return
     */
    public boolean insertObject(String key ,Object obj ){
        return insertObject(key,obj,0L);
    }


    /**
     * 添加 map
     * @param key
     * @param map
     * @param validTime
     * @return
     */
    public boolean hMSet(final String key, HashMap<String,Object> map, final long validTime) {
        try {
            final Map<byte[], byte[]> hashes =new HashMap<>();
            //设置map值
            for(String str:map.keySet()){
                hashes.put(redisTemplate.getStringSerializer().serialize(str),redisTemplate.getStringSerializer().serialize(JSON.toJSONString(map.get(str))));
            }
            //执行redis操作
            boolean result = redisTemplate.execute((RedisConnection redisConnection) -> {
                byte[] redisKey = redisTemplate.getStringSerializer().serialize(key);
                //插入redis
                redisConnection.hMSet(redisKey, hashes);
                //如果有有效时间，则设置过期时间
                if (validTime > 0){
                    redisTemplate.expire(key, validTime, TimeUnit.SECONDS);
                }
                return true;
            });
            return result;
        } catch (Exception e){
            logger.error("添加redis出现异常", e);
        }
        return false;
    }



    /**************************************************************************************查询*************************************************************************/

    /**
     * 查询string-根据key和对象类型查询redis中的对象
     * @param key
     * @param clazz
     * @param <T>
     * @return 对象
     */
    public <T> T queryObjectByKey(String key, Class<T> clazz) {
        try {
            String resultStr = queryObjectByKey(key);
            if (StringUtils.isBlank(resultStr)){
                return null;
            }
            T value = JSONObject.parseObject(resultStr, clazz);
            return value;
        } catch (Exception e){
            logger.error("查找redis异常，key：{} 异常", key, e);
        }
        return null;
    }

    /**
     * 查询string
     * @param key
     * @return String
     */
    public String queryObjectByKey(final String key) {
        try {
            String resultStr = redisTemplate.execute((RedisConnection redisConnection) -> {
                byte[] redisKey = redisTemplate.getStringSerializer().serialize(key);
                if (redisConnection.exists(redisKey)){
                    byte[] value = redisConnection.get(redisKey);
                    return redisTemplate.getStringSerializer().deserialize(value);
                }
                return null;
            });
            return resultStr;
        } catch (Exception e){
            logger.error("查找redis异常，key：{} 异常", key, e);
        }
        return null;
    }

    /**
     * 查询map
     * @param key
     * @param clazz map中的clazz
     * @param <T>
     * @return
     */
    public <T> Map<String,T> getMapAll(final String key ,final Class<T> clazz) {
        try {
            Map<String,T> result = redisTemplate.execute((RedisConnection redisConnection) -> {
                byte[] redisKey = redisTemplate.getStringSerializer().serialize(key);
                //查询出map值
                Map<byte[], byte[]>  map = redisConnection.hGetAll(redisKey);
                //转换结果
                if(map !=null && map.size() > 0){
                    Map<String,T> result1 = new HashMap<>();
                    for (byte[] b: map.keySet()){
                        String resultKey = redisTemplate.getStringSerializer().deserialize(b);
                        T resultValue = JSONObject.parseObject(redisTemplate.getStringSerializer().deserialize(map.get(b)), clazz);
                        result1.put(resultKey, resultValue);
                    }
                    return result1;
                }
                return null;
            });
            return result;
        } catch (Exception e){
            logger.error("getMapAll redis异常", e);
        }
        return null;
    }


    /**************************************************************************************删除*************************************************************************/
    /**
     * 删除str
     * @param key
     */
    public boolean del(String key){
        try{
            return redisTemplate.execute((RedisConnection redisConnection) -> {

                byte[] redisKey = redisTemplate.getStringSerializer().serialize(key);

                redisConnection.del(redisKey);

                return true;
            });
        }catch (Exception e){
            logger.error("删除redis对象异常",e);
        }
       return false;
    }

}
